package main

import (
	"bufio"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/panjf2000/ants/v2"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

const (
	ProductName="伍邪数据生成工具 For Mysql"
	Version="1.0.0.20200921"
	Author= "伍邪 QQ:16349023"
	ChangeLog="优化为多线程、批量生成数据"
)

var Db *sqlx.DB
var dbConfig *DbConfig
var appConfig *AppConfig

// 初始化链接
func init() {
	appConfig = new(AppConfig)
	appConfig.getConfig()

	dbConfig = &DbConfig{}
	dbConfig.getDbConf()

	if appConfig.ThreadCount > dbConfig.MaxOpenConns * 4/5 {
		appConfig.ThreadCount=dbConfig.MaxOpenConns * 4/5
	}


	dbSource :=fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s",dbConfig.User,dbConfig.Pwd,dbConfig.Host,dbConfig.Dbname,dbConfig.Charset)
	//"wyy:123456@tcp(127.0.0.1:3306)/test?charset=utf8"
	database, err := sqlx.Open("mysql", dbSource)
	if err != nil {
		fmt.Println("open mysql failed,", err)
		return
	}
	database.SetMaxOpenConns(dbConfig.MaxOpenConns)
	database.SetMaxIdleConns(dbConfig.MaxIdleConns)
	var duration time.Duration= time.Duration(dbConfig.ConnMaxLifetime)*time.Minute
	database.SetConnMaxLifetime(duration)
	//fmt.Printf("DriverName:%v",Db.DriverName())
	Db = database
	//defer Db.Close()  // 注意这行代码要写在上面err判断的下面

}

func outPrint(generateChan chan GenerateMessage,total *int64, beginTime time.Time, config AppConfig)  {
	lastTime := time.Now()
	enablePrint := true
	for{
		generateMessage := <- generateChan
		if generateMessage.LastId == -1 {
			enablePrint =false
		}else if generateMessage.LastId == 0 {
			enablePrint =true
		}
		*total += int64(config.BatchCount)

		if !enablePrint {
			continue
		}

		now := time.Now()

		preDuration :=now.Sub(lastTime)
		if preDuration.Milliseconds() > config.PrintPeriod {
			lastTime = now

			duration :=generateMessage.EndTime.Sub(beginTime)
			var secTotal int64=int64(duration.Seconds())
			if secTotal < 1{
				secTotal = 1
			}
			perSec := *total / secTotal
			if duration.Seconds() < 60 {
				fmt.Printf("\r %s total: %d条, per second: %d条, useTime: %.2f秒,succ lastId:%d",
					time.Now().Format("2006-01-02 15:04:05"),
					*total, perSec, duration.Seconds(), generateMessage.LastId)
			}else if duration.Minutes() < 60 {
				fmt.Printf("\r %s total: %d条, per second: %d条, useTime: %.2f分钟,succ lastId:%d",
					time.Now().Format("2006-01-02 15:04:05"),
					*total, perSec, duration.Minutes(), generateMessage.LastId)
			}else if duration.Hours() < 24 {
				fmt.Printf("\r %s total: %d条, per second: %d条, useTime: %.2f小时,succ lastId:%d",
					time.Now().Format("2006-01-02 15:04:05"),
					*total, perSec, duration.Hours(), generateMessage.LastId)
			}else {
				fmt.Printf("\r %s total: %d条, per second: %d条, useTime: %.2f天,succ lastId:%d",
					time.Now().Format("2006-01-02 15:04:05"),
					*total, perSec, duration.Hours()/24, generateMessage.LastId)
			}
		}



	}

}

func main() {
	defer Db.Close()  // 注意这行代码要写在上面err判断的下面
	defer InsertDataEnd()




	fmt.Println(" Devlang:","golang 1.14")
	fmt.Printf(" 应用名称:  %v \n", ProductName)
	fmt.Printf("   开发者:  %v \n", Author)
	fmt.Printf("     版本:  %v \n", Version)
	fmt.Printf(" 更新说明:  %v \n", ChangeLog)
	fmt.Println("----生成配置信息>>--------------------------------------")
	fmt.Printf("  写入一批后延时:  %v毫秒\n", appConfig.SleepMilliSecond)
	fmt.Printf(" 插入数据Sql语句:  %v \n", appConfig.InsertSql)
	fmt.Printf("  插入数据值类型:   %v \n", appConfig.InsertValueTypes)
	fmt.Printf("     插入数据值:   %v \n", appConfig.InsertValues)
	fmt.Printf("        线程数:   %v \n", appConfig.ThreadCount)
	fmt.Printf("   每批插入条数:   %v条 \n", appConfig.BatchCount)
	fmt.Printf("   打印输出间隔周期:   %v毫秒 \n", appConfig.PrintPeriod)
	fmt.Println("----<<生成配置信息--------------------------------------")
	var totalCount int64=0
	now :=time.Now()
	fmt.Printf(" 开始时间: %v \n", now.Format("2006-01-02 15:04:05"))
	//fmt.Println("*************************开始生成数据************************")

	generateChan := make(chan GenerateMessage,4096)

	go outPrint(generateChan,&totalCount,now,*appConfig)

	p, _ := ants.NewPoolWithFunc(appConfig.ThreadCount, func(goroutineId interface{}) {
		id,ok :=goroutineId.(uint32)
		if ok {
			GenerateData(generateChan, id, *appConfig)
		}
	})

	for i :=0; i< appConfig.ThreadCount; i++ {
		//go GenerateData(generateChan, i, *appConfig)
		//p.Submit(GenerateData
		p.Invoke(uint32(i))
	}
	defer p.Release()

	sc := make(chan os.Signal, 1)
	signal.Notify(sc,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
		syscall.SIGPIPE,
	)
	var waitGroup sync.WaitGroup
	waitGroup.Add(1)

	go func() {
		defer waitGroup.Done()
		for {
			sig := <-sc
			if sig == syscall.SIGINT || sig == syscall.SIGTERM || sig == syscall.SIGQUIT {
				now :=time.Now()
				generateMessage := GenerateMessage{}
				generateMessage.GoroutineId=5
				generateMessage.LastId =-1
				generateMessage.EndTime = now
				generateChan <- generateMessage
				fmt.Printf("\nGot signal %d, quit\n", sig)
				fmt.Printf("\n确定关闭么？ y/n:")
				input := bufio.NewScanner(os.Stdin)
				if input.Scan() && input.Text() == "y" {
					break
				}else {
					generateMessage.LastId = 0
					generateChan <- generateMessage
				}
			}
		}
	}()

	//等待预设数量全部提交,阻止主线程结束
	waitGroup.Wait()
}